package com.biostime.qdingding.modules.qd.service;

import java.io.ByteArrayOutputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.biostime.qdingding.common.config.Global;
import com.biostime.qdingding.modules.qd.api.dto.ContractDto;
import com.biostime.qdingding.modules.qd.api.dto.QueryPayOrderDto;
import com.biostime.qdingding.modules.qd.api.dto.UniformOrderDto;
import com.biostime.qdingding.modules.qd.api.dto.WeixinEmployDto;
import com.biostime.qdingding.modules.qd.api.dto.WeixinPayDto;
import com.biostime.qdingding.modules.qd.entity.Pay;
import com.biostime.qdingding.modules.qd.entity.Transrecord;
import com.biostime.qdingding.modules.sys.entity.User;

/**
 * @author chenweicheng
 *
 */
@Transactional
@Service
public class WeixinPayService {
	
	//AppKey u99P0asgnWCCEBm7HePvFg68ekkWouiS
	private final static String Key = "u99P0asgnWCCEBM7HePvFg68ekkWouiS";
	
	//AppId wx6b181968c63726de
	private final static String AppID ="wx301e533e3bd774b3";
	
	//商户号 1278337201
	private final static String customerId = "1303166401";
	
	public static Logger logger=Logger.getLogger("api"); 
	
	@Autowired
	private CommentService commentService;
	
	@Autowired
	private ContractService contractService;
	
	@Autowired
	private TransrecordService  recordService;
	
	@Autowired
	private PayService payService;
	
	
    public static String getKey() {
		return Key;
	}

	public static String getAppid() {
		return AppID;
	}

	public static String getCustomerid() {
		return customerId;
	}
	

	/** 
     * MD5等加密算法
     */  
	private static String byteArrayToHexString(byte b[]) {  
        StringBuffer resultSb = new StringBuffer();  
        for (int i = 0; i < b.length; i++)  
            resultSb.append(byteToHexString(b[i]));  
  
        return resultSb.toString();  
    }  
  
    private static String byteToHexString(byte b) {  
        int n = b;  
        if (n < 0)  
            n += 256;  
        int d1 = n / 16;  
        int d2 = n % 16;  
        return hexDigits[d1] + hexDigits[d2];  
    }  
  
    public static String MD5Encode(String origin, String charsetname) {  
        String resultString = null;  
        try {  
            resultString = new String(origin);  
            MessageDigest md = MessageDigest.getInstance("MD5");  
            if (charsetname == null || "".equals(charsetname))  
                resultString = byteArrayToHexString(md.digest(resultString  
                        .getBytes()));  
            else  
                resultString = byteArrayToHexString(md.digest(resultString  
                        .getBytes(charsetname)));  
        } catch (Exception exception) {  
        }  
        return resultString;  
    }  
  
    private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",  
        "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };  
    
    
    /** 
     * 微信支付签名算法sign 
     * @param characterEncoding 
     * @param parameters 
     * @return 
     */  
    @SuppressWarnings({ "rawtypes" })  
    public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){  
        StringBuffer sb = new StringBuffer();  
        Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序（升序）  
        Iterator it = es.iterator();  
        while(it.hasNext()) {  
            Map.Entry entry = (Map.Entry)it.next();  
            String k = (String)entry.getKey();  
            Object v = entry.getValue();  
            if(null != v && !"".equals(v)   
                    && !"sign".equals(k) && !"key".equals(k)) {  
                sb.append(k + "=" + v + "&");  
            }  
        }  
        sb.append("key=" +Key);  
        String sign = MD5Encode(sb.toString(), characterEncoding).toUpperCase();  
        return sign;  
    }   	
	
    
	/**
	 * 用于返回序列化后的xml
	 * @param pay
	 * @return
	 * @throws JAXBException
	 * @throws UnsupportedEncodingException
	 */
	public static String returnXml(WeixinPayDto pay) throws JAXBException, UnsupportedEncodingException{
		
		//用于产生签名
        SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();  
        parameters.put("appid", pay.getAppid());  
        parameters.put("mch_id", pay.getMch_id());
        parameters.put("attach", pay.getAttach()); 
        parameters.put("body", pay.getBody());  
        parameters.put("nonce_str", pay.getNonce_str());  
        parameters.put("out_trade_no", pay.getOut_trade_no());  
        parameters.put("total_fee", pay.getTotal_fee());  
        parameters.put("spbill_create_ip", pay.getSpbill_create_ip());  
        parameters.put("notify_url",pay.getNotify_url());  
        parameters.put("trade_type", pay.getTrade_type()); 
        String characterEncoding = "UTF-8";  
        String mySign = createSign(characterEncoding,parameters);  
        pay.setSign(mySign); 	
        	
        //java对象转xml
        JAXBContext context = JAXBContext.newInstance(pay.getClass());  
        Marshaller marshaller = context.createMarshaller();   
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        marshaller.marshal(pay, baos);
        String xmlObj = new String(baos.toByteArray());
        String xmlObj2=(new String(xmlObj.getBytes(),"utf-8")).trim();
        System.out.println(xmlObj2); 
        
        return xmlObj2;
	}
		
	/**
	 * 微信统一下单
	 * @param request
	 * @return
	 * @throws UnsupportedEncodingException
	 * @throws JAXBException
	 */
	public  String payMoney(HttpServletRequest request) throws UnsupportedEncodingException, JAXBException{

		WeixinPayService.logger.info("------------第一步：进入微信统一下单------------");
		Double payMoney = Double.valueOf(request.getParameter("money"));
		WeixinPayDto pay = new WeixinPayDto();
		pay.setAppid(WeixinPayService.getAppid());
		pay.setMch_id(WeixinPayService.getCustomerid());
		
		//项目url
		String projectUrl = request.getRequestURI();
		String url[] = projectUrl.split("/");
		if(url.length>3){
			projectUrl = url[0]+"/"+url[1]+"/"+url[2];
		}
		String serverUrl ="http://"+Global.getConfig("domain")+"/qdingding-webapp"+Global.getConfig("adminPath")+
				"/qd/api/pay/payReturn";
				
		pay.setNotify_url(serverUrl);
		pay.setTrade_type("APP");
		pay.setBody("支付合同余额");
									
		//App端IP地址
		if(commentService.getIpAddress(request).equals("0:0:0:0:0:0:0:1")){
			pay.setSpbill_create_ip("123.12.12.123"); 
		}else{
			pay.setSpbill_create_ip(commentService.getIpAddress(request));
		}
				
		pay.setTotal_fee((int)(payMoney*100));  
		
		//随机32位字符串
		pay.setNonce_str(WeixinPayService.MD5Encode(String.valueOf(((Math.random() * 9 + 1) * 100000)), "utf-8"));
		
		//商家订单号
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmsszzz");
		String tradeNo = String.valueOf(((int)(Math.random() * 9 + 1) * 1000))+sdf.format(new Date())+String.valueOf((int)
				((Math.random() * 9 + 1) * 10000));	
		pay.setOut_trade_no(tradeNo);
		
		 WeixinPayService.logger.info("产生商家订单号："+pay.getOut_trade_no());
			
		//javabean转xml
		String xmlObj = WeixinPayService.returnXml(pay);
		
		//发送 POST请求
		String sr = HttpRequestService.sendPost("https://api.mch.weixin.qq.com/pay/unifiedorder", xmlObj);
		String str = (new String(sr.getBytes(), "utf-8")).trim();
		
		//xml转javabean
        JAXBContext context = JAXBContext.newInstance(WeixinPayDto.class);  
        Unmarshaller unmarshaller = context.createUnmarshaller();  
        WeixinPayDto dto = (WeixinPayDto)unmarshaller.unmarshal(new StringReader(str));  
                
        //调起支付接口dto
        WeixinEmployDto employDto = new WeixinEmployDto();
        employDto.setAppid(dto.getAppid());
        employDto.setNoncestr(dto.getNonce_str());
        employDto.setPackageweixin("Sign=WXPay");
        employDto.setPrepayid(dto.getPrepay_id());
        employDto.setPartnerid(dto.getMch_id());
        employDto.setTimestamp(new Date().getTime()/1000);
        
		//用于产生签名
        SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();  
        parameters.put("appid", employDto.getAppid());  
        parameters.put("partnerid", employDto.getPartnerid());
        parameters.put("prepayid", employDto.getPrepayid());  
        parameters.put("package", employDto.getPackageweixin());   
        parameters.put("noncestr", employDto.getNoncestr());  
        parameters.put("timestamp", employDto.getTimestamp());   
        String characterEncoding = "UTF-8";  
        String mySign = createSign(characterEncoding,parameters);    
        employDto.setSign(mySign); 
        
        //java对象转xml
        JAXBContext context2 = JAXBContext.newInstance(employDto.getClass());  
        Marshaller marshaller = context2.createMarshaller();   
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        marshaller.marshal(employDto, baos);
        String xmlObj2 = new String(baos.toByteArray());
        String xmlObj3=(new String(xmlObj2.getBytes(),"utf-8")).trim();        
          
        //插入支付记录表
        Pay payEntity = new Pay();
        payEntity.setMemberId(Integer.parseInt(request.getParameter("userId")));
        payEntity.setStudentId(Integer.parseInt(request.getParameter("studentId")));
        payEntity.setOutTradeNo(pay.getOut_trade_no()); 
        payEntity.setCreateBy(new User("0")); 
        payEntity.setCreateDate(new Date());
        payEntity.setUpdateBy(new User("0"));
        payEntity.setUpdateDate(new Date());
        payEntity.setPlatform(0); 
        payEntity.setCenterId(Integer.parseInt(request.getParameter("centerId")));
        payService.save(payEntity); 
             
        String return_code = "失败";
        String return_msg = "失败";
        if(dto != null){ 
        	return_code = dto.getReturn_code();
        	return_msg = dto.getReturn_msg();
        }	
		
		WeixinPayService.logger.info("微信统一下单："+"   "+"支付总金额:"+(pay.getTotal_fee()/(double)100)+"元"+"   "+"状况："
				+return_code+"   "+"具体描述："+return_msg);	 
		       		
		return xmlObj3;
		
	} 
	
	/**
	 * 微信回调
	 * 
	 */	
	public void wexinPayReturn(String body) throws JAXBException, UnsupportedEncodingException{
		
		WeixinPayService.logger.info("------------第二步：进入微信回调------------");
        JAXBContext context = JAXBContext.newInstance(UniformOrderDto.class);  
        Unmarshaller unmarshaller = context.createUnmarshaller();  
        UniformOrderDto dto = (UniformOrderDto)unmarshaller.unmarshal(new StringReader(body)); 
                
		//用于检验请求签名是否正确
        SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();  
        parameters.put("appid", dto.getAppid());  
        parameters.put("mch_id", dto.getMch_id());
        parameters.put("bank_type", dto.getBank_type()); 
        parameters.put("cash_fee", dto.getCash_fee());  
        parameters.put("fee_type", dto.getFee_type());  
        parameters.put("is_subscribe", dto.getIs_subscribe());  
        parameters.put("nonce_str", dto.getNonce_str());  
        parameters.put("openid", dto.getOpenid());  
        parameters.put("out_trade_no",dto.getOut_trade_no());  
        parameters.put("result_code", dto.getResult_code()); 
        parameters.put("return_code", dto.getReturn_code()); 
        parameters.put("time_end", dto.getTime_end()); 
        parameters.put("total_fee", dto.getTotal_fee()); 
        parameters.put("trade_type", dto.getTrade_type()); 
        parameters.put("transaction_id", dto.getTransaction_id()); 
        String characterEncoding = "UTF-8";  
        String mySign = createSign(characterEncoding,parameters);  
        
        //验证签名
        if(dto.getSign().equals(mySign)){
        	
        	WeixinPayService.logger.info("------------签名正确，该回调安全----------");
            WeixinPayService.logger.info("回调商家订单号："+dto.getOut_trade_no());
            
            String userId = "0";
            String studentId = "0";
            Pay payEntity = payService.findByTradeNo(dto.getOut_trade_no());
            if(payEntity != null && recordService.apiGetByCode(dto.getOut_trade_no()) == null){    
            	
                //确认支付	          
    			userId = String.valueOf(payEntity.getMemberId());
            	studentId = String.valueOf(payEntity.getStudentId());
            	
                WeixinPayService.logger.info("payEntity"+payEntity);
                
                //更新支付记录
                payEntity.setTransactionId(dto.getTransaction_id()); 
                payService.updateTrasactionId(payEntity);
        		payConfirm(userId,studentId,dto.getOut_trade_no());
        		
            }else{
            	WeixinPayService.logger.info("未找到商家订单号："+dto.getOut_trade_no());
            }
            
        }else{
        	WeixinPayService.logger.info("---------------------签名不正确，回调失败--------------------");
        }
                

    
	}
	
	/**
	 * 微信确认支付
	 * (一)先调用微信接口查询订单
	 * (二)若成功支付，则添加收支记录
	 */
	public HashMap<String,Object> payConfirm(String userId,String studentId,String out_trade_no) throws JAXBException, UnsupportedEncodingException{
         
		WeixinPayService.logger.info("------------第三步：通过商家订单号查询订单------------"); 
		boolean flag = false;
		int fee = 0;
		String errMessage = null;
		String return_msg = null;
		
		QueryPayOrderDto dto = new QueryPayOrderDto();
		dto.setAppid(WeixinPayService.AppID);
		dto.setMch_id(WeixinPayService.customerId);
		dto.setOut_trade_no(out_trade_no); 
		dto.setNonce_str(WeixinPayService.MD5Encode(String.valueOf(((Math.random() * 9 + 1) * 100000)), "utf-8")); 
		
		//用于产生签名
        SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();  
        parameters.put("appid", WeixinPayService.AppID);  
        parameters.put("mch_id", WeixinPayService.customerId);
        parameters.put("nonce_str", dto.getNonce_str());   
        parameters.put("out_trade_no", dto.getOut_trade_no());            
        String characterEncoding = "UTF-8";  
        String mySign = createSign(characterEncoding,parameters);  
        dto.setSign(mySign); 
        
        //java对象转xml
        JAXBContext context = JAXBContext.newInstance(dto.getClass());  
        Marshaller marshaller = context.createMarshaller();   
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        marshaller.marshal(dto, baos);
        String xmlObj = new String(baos.toByteArray());
        String xmlObj2=(new String(xmlObj.getBytes(),"utf-8")).trim();
        
		//发送POST请求,查询支付结果
		String sr = HttpRequestService.sendPost("https://api.mch.weixin.qq.com/pay/orderquery", xmlObj2);
		String str = (new String(sr.getBytes(), "utf-8")).trim();
		
		if(str != null){
			
			//xml转javabean
	        JAXBContext context2 = JAXBContext.newInstance(UniformOrderDto.class);  
	        Unmarshaller unmarshaller = context2.createUnmarshaller();  
	        UniformOrderDto dto2 = (UniformOrderDto)unmarshaller.unmarshal(new StringReader(str));  
	        
        	//如果返回码为success 和 支付状态为success
	        if(dto2 != null){
    			return_msg = dto2.getErr_code();
	        	if(dto2.getReturn_code().equals("SUCCESS")){
	        		if(dto2.getResult_code().equals("SUCCESS") && dto2.getTrade_state().equals("SUCCESS")){
	        			
	        			//用于检验请求签名是否正确
	        	        SortedMap<Object,Object> parameters2 = new TreeMap<Object,Object>();  
	        	        parameters2.put("appid", dto2.getAppid());  
	        	        parameters2.put("mch_id", dto2.getMch_id());
	        	        parameters2.put("bank_type", dto2.getBank_type()); 
	        	        parameters2.put("cash_fee", dto2.getCash_fee());  
	        	        parameters2.put("fee_type", dto2.getFee_type());  
	        	        parameters2.put("is_subscribe", dto2.getIs_subscribe());  
	        	        parameters2.put("nonce_str", dto2.getNonce_str());  
	        	        parameters2.put("openid", dto2.getOpenid());  
	        	        parameters2.put("out_trade_no",dto2.getOut_trade_no());  
	        	        parameters2.put("result_code", dto2.getResult_code()); 
	        	        parameters2.put("return_code", dto2.getReturn_code()); 
	        	        parameters2.put("time_end", dto2.getTime_end()); 
	        	        parameters2.put("total_fee", dto2.getTotal_fee()); 
	        	        parameters2.put("trade_type", dto2.getTrade_type()); 
	        	        parameters2.put("transaction_id", dto2.getTransaction_id()); 
	        	        parameters2.put("trade_state", dto2.getTrade_state()); 
	        	        parameters2.put("return_msg", dto2.getReturn_msg()); 
	        	        parameters2.put("attach", dto2.getAttach()); 
	        	        String characterEncoding2 = "UTF-8";  
	        	        String mySign2 = createSign(characterEncoding2,parameters2);  	        			
	        			
	        	        if(mySign2.equals(dto2.getSign())){
	        	        	WeixinPayService.logger.info("------------签名正确，该微信订单信息安全----------");
			        		fee = dto2.getTotal_fee();
			        		flag = true;
	        	        }else{
	        	        	WeixinPayService.logger.info("------------签名不正确，获取微信订单信息失败----------");
	        	        }
	        			
	        		}
	        	}
	        }
		}
		
		//添加收支记录
		if(flag ==  true){
	    	int centerId = 0;
	    	int contractId = 0;
			List<ContractDto> listContract=contractService.apiGetContract(studentId);
			if(listContract != null && !listContract.isEmpty()){
				centerId = listContract.get(0).getCenterId();
				contractId = listContract.get(0).getId(); 
			}
			
			Transrecord tr = new Transrecord();
			tr.setMoney(fee/(double)100); // 交易金额
			tr.setDescription(String.format("微信支付", tr.getMoney())); //描述	
			tr.setPayCode(out_trade_no); //支付订单号
			tr.setCenterId(centerId);//早教中心
			tr.setContractId(contractId); //合同id
			tr.setMemberId(Integer.parseInt(userId)); //会员编号
			tr.setStudentId(Integer.parseInt(studentId)); //学员编号
			//交易类型 支的有：普通课程、户外课程； 收的有：1充值（即合同支付）、2请假资格返还、3取消资格返还，
			tr.setType("0");
			tr.setPayType("1"); //支付方式（微信，支付宝，银联，现金）
			recordService.apiPay(tr); 
			
			errMessage = "支付成功";
			
		}else{
			errMessage = "支付失败";
		}
		
		//打印日志
		WeixinPayService.logger.info("-------------用户Id："+userId+", 学员Id："+studentId+"-----------"); 
		WeixinPayService.logger.info("微信"+errMessage+"："+"   "+"成交金额:"+(fee/(double)100)+"元"+"   "+"商户订单号："+out_trade_no
				+"   "+"具体描述："+return_msg);	 
		
		HashMap<String,Object> map = new HashMap<String,Object>();
		map.put("errMessage", return_msg);
		map.put("flag", flag);
		
		return map;
        	     		
	}
	
}
